/**
* file   Collider.h
* author andematunum@gmail.com
* date   Mar 22 déc 2009 14:30:00 CET
* brief  Various collision routines.
*        Note that SDL 1.3 defines
*        rectangle-rectangle and rectangle-segment
*        intersection routines
*/
#include "SDL.h"
#include "Vector2d.h"

struct ColliderData {
  int config;
  Vector2d impact_vector;
  // Store the address of the first
  // object implied in the collision.
  // Not made in the present classes.
  void *from_object;
};

class Collider {
public:
  /**
    * Circle-Rectangle collision detection.
    * This routine assumes that the center of
    * the circle is *never* inside the rectangle,
    * and gives the topology of the intersection
    * as follows:
    *
    * 0  1  2
    * 7     3
    * 6  5  4
    *
    * Where 0,2,4,6 are corner collisions whereas
    * 1,3,5,7 are edge collisions.
    *
    * \param[out] config -1 if there was no collision,
                  or a number in {0,1,2,3,4,5,6,7}
    * \return true if a collision occured, false otherwise
    */
    static bool circle_2_rectangle (
      const SDL_Point& center,
      int radius,
      const SDL_Rect& rect,
      ColliderData* data) {

      int cx = center.x;
      int cy = center.y;
      int x1 = rect.x;
      int y1 = rect.y;
      int x2 = x1+rect.w;
      int y2 = y1+rect.h;
      int xmid = x2 - static_cast<int>((double)rect.w/2.);
      int ymid = y2 - static_cast<int>((double)rect.h/2.);
      int config = -1;

      // Check for edge collision
      if ((x1<=cx) && (cx<x2)) {
        if ((cy>=y1-radius) && (cy<ymid)) {//y1)) {
          config = 1;
        }
        else if ((cy<=y2+radius) && (cy>ymid)) {//y2)) {
          config = 5;
        }
      }
      else if ((y1<=cy) && (cy<y2)) {
        if ((cx>=x1-radius) && (cx<xmid)) {//x1)){
          config = 7;
        }
        else if ((cx<=x2+radius) && (cx>xmid)) {//x2)) {
          config = 3;
        }
      }
      // Check for corner collision
      if (config == -1) {
        SDL_Point corner = {-1,-1};
        int potential_config = -1;

        if (cx<x1) {
          if (cy<y1) {
            corner.x = x1;
            corner.y = y1;
            data->impact_vector.set(
              x1 - cx, y1 - cy
            );
            potential_config = 0;
          }
          else if (cy>y2) {
            corner.x = x1;
            corner.y = y2;
            data->impact_vector.set(
              x1 - cx, y2 - cy
            );
            potential_config = 6;
          }
        }
        else if (cx>x2) {
          if (cy<y1) {
            corner.x = x2;
            corner.y = y1;
            data->impact_vector.set(
              x2 - cx, y1 - cy
            );
            potential_config = 2;
          }
          else if (cy>y2) {
            corner.x = x2;
            corner.y = y2;
            data->impact_vector.set(
              x2 - cx, y2 - cy
            );
            potential_config = 4;
          }
        }
        if ((cx-corner.x)*(cx-corner.x) + (cy-corner.y)*(cy-corner.y)
              < radius*radius) {
              config = potential_config;
        }
      }
      data->config = config;
      return config != -1;
    }

    static bool circle_2_circle (
      const SDL_Point& center1, int radius1,
      const SDL_Point& center2, int radius2,
      ColliderData* data) {
    
      int cx1 = center1.x;
      int cy1 = center1.y;
      int cx2 = center2.x;
      int cy2 = center2.y;
      int vx = cx1 - cx2;
      int vy = cy1 - cy2;
      double dist_sq = vx*vx + vy*vy;
      int sum_radius = radius1 + radius2;
      bool intersect = dist_sq <= sum_radius*sum_radius;
      
      if (intersect) {
        data->impact_vector.set (vx, vy);
      }
      return intersect;
    }
};

